//
//  XWXSqliteModelTool.m
//  XWXSqlite
//
//  Created by 许卫星 on 17/9/4.
//  Copyright © 2017年 wxxu. All rights reserved.
//

#import "XWXSqliteModelTool.h"
#import <objc/message.h>
#import "NSString+XWXSqliteSQL.h"
#import "XWXSqliteModelDelegate.h"
#import "XWXSqliteConfiguration.h"

@implementation XWXSqliteModelTool

/**
 获取表格名称
 */
+ (NSString *)tableNameWithModelClass: (Class)cls
{
    return [NSStringFromClass(cls) lowercaseString];
}
/**
 获取临时表格名称
 */
+ (NSString *)tempTableNameWithModelClass: (Class)cls
{
    return [[NSStringFromClass(cls) lowercaseString] stringByAppendingString:@"_tmp"];
}


/**
 类成员变量名称与类型数据
 @param cls 类
 @return 类成员变量名称与类型
 */
+ (NSMutableDictionary *)classIvarNameTypeDic:(Class)cls
{
    //1.获取类的所有成员变量
    unsigned int outCount = 0;
    Ivar *ivarList = class_copyIvarList(cls, &outCount);
    
    if ([self isIgnorePrimaryKey:cls]) {
        return nil;
    }
    
    //过滤字段
    NSArray *ignoreNames = nil;
    if ([cls conformsToProtocol:@protocol(XWXSqliteModelDelegate)] && [[cls new] respondsToSelector:@selector(ignoreIvarNames)]) {
        ignoreNames = [[cls alloc] ignoreIvarNames];
    }
    
    NSMutableDictionary *nameTypeDic = [[NSMutableDictionary alloc] init];
    for (int i = 0; i < outCount; i++) {
        Ivar ivar = ivarList[i];
        //获取成员变量名称
        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];

        //获取成员变量类型
        NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        ivarType = [ivarType safeIvarType];
        if ([ivarName hasPrefix:@"_"] && ivarName.length > 1) {
            ivarName = [ivarName substringFromIndex:1];
        }
        //过滤字段
        if ([ignoreNames containsObject:ivarName]) {
            continue;
        }
        
        [nameTypeDic setObject:ivarType forKey:ivarName];
    }
    
    //如果没有指定主键，默认_id
    //主键
    NSString *primaryKey = [XWXSqliteModelTool primaryKeyWithCls:cls];
    if ([primaryKey isEqualToString:kPrimaryKey]) {
        [nameTypeDic setObject:@"i" forKey:kPrimaryKey];
    }
    
    return nameTypeDic;
}

/**
 类成员变量名称与对应的Sqlite类型数据
 @param cls 类
 @return 类成员变量名称与类型
 */
+ (NSDictionary *)modelIvarNameSqlTypeDic: (Class)cls
{
    NSMutableDictionary *nameTypeDic = [self classIvarNameTypeDic:cls];
    NSDictionary *sqliteTypeDic = [self ocTypeToSqliteTypeDic];
    
    [nameTypeDic enumerateKeysAndObjectsUsingBlock:^(NSString*  _Nonnull key, NSString*  _Nonnull obj, BOOL * _Nonnull stop) {
        nameTypeDic[key] = sqliteTypeDic[obj];
    }];
    
    return nameTypeDic;
}

/**
 类成员变量[name:value]
 @param cls 类
 @return 名称和值
 */
+ (NSDictionary *)modelIvarNameValueDic:(id)model
{
    NSDictionary *nameTypeDic = [self classIvarNameTypeDic:[model class]];
    NSArray *nameArray = [nameTypeDic allKeys];
    
    return [self modelIvarNameValueDicWithNames:nameArray model:model isUpdate:NO];
}

//获取传过来的键值对
+ (NSDictionary *)modelIvarNameValueDicWithNames:(NSArray *)nameArray model:(id)model isUpdate:(BOOL)isUpdate
{
    NSMutableDictionary *nameValueDic = [[NSMutableDictionary alloc] init];
    NSInteger count = nameArray.count;
    for (int i = 0; i < count; i++) {
        NSString *key = nameArray[i];
        if ([key isEqualToString:kPrimaryKey]) {
            continue;
        }
        id value = [model valueForKeyPath:key];
        if ([value isKindOfClass:[NSString class]]) {
            value = [NSString stringWithFormat:@"'%@'", [value safeSQLEncode]];
        } else if ([value isKindOfClass:[NSNull class]] || value == 0x0) {
            if (isUpdate) {
                continue;
            }
            value = @"NULL";
        } else {
            value = [NSString stringWithFormat:@"'%@'", value];
        }
        [nameValueDic setObject:value forKey:key];
    }
    return nameValueDic;
}

//修改模型数据
+ (NSDictionary *)updateModelIvarNameValueDic:(id)model
{
    Class cls = [model class];
    if ([self isIgnorePrimaryKey:cls]) {
        return nil;
    }
    if ([self isUpdatePrimaryKey:cls]) {
        return nil;
    }
    //获取需要修改的字段
    //1.从updateIvarNames中取，如果没有实现updateIvarNames，默认取所有的
    NSArray *updateNames;
    if ([cls conformsToProtocol:@protocol(XWXSqliteModelDelegate)] && [[cls new] respondsToSelector:@selector(updateIvarNames)]) {
        updateNames = [[cls alloc] updateIvarNames];
    }else{
        updateNames = [self classIvarNames:cls];
    }
    return [self modelIvarNameValueDicWithNames:updateNames model:model isUpdate:YES];
}

/**
 类成员变量名称
 @param cls 类
 @return 名称拼接字符串
 */
+ (NSString *)modelIvarNameStr:(Class)cls
{
    NSDictionary *nameTypeDic = [self classIvarNameTypeDic:cls];
    NSArray *nameArray = [nameTypeDic allKeys];
    
    return [nameArray componentsJoinedByString:@","];;
}

/**
 类成员变量Value
 @param cls 类
 @return value拼接字符串
 */
+ (NSString *)modelIvarValueStr:(id)model
{
    NSDictionary *nameTypeDic = [self modelIvarNameValueDic:model];
    NSArray *nameArray = [nameTypeDic allValues];
    
    return [nameArray componentsJoinedByString:@","];
}


/**
 字段名称和sql类型, 拼接的用户创建表格的字符串
 
 @param cls 类名
 @return 字符串 如: name text,age integer,score real
 */
+ (NSString *)columnNamesAndTypesStr:(Class)cls
{
    NSDictionary *nameTypeDic = [self modelIvarNameSqlTypeDic:cls];
    NSMutableArray *nameTypeArray = [[NSMutableArray alloc] init];
    [nameTypeDic enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        NSString *keyValue = [NSString stringWithFormat:@"%@ %@",key,obj];
        [nameTypeArray addObject:keyValue];
    }];
    
    NSString *namesypesStr = [nameTypeArray componentsJoinedByString:@","];
    return namesypesStr;
}

//获取主键
+ (NSString *)primaryKeyWithCls:(Class)cls
{
    //主键
    NSString *primaryKey = kPrimaryKey;//默认主键为_id
    if ([cls conformsToProtocol:@protocol(XWXSqliteModelDelegate)] && [[cls new] respondsToSelector:@selector(primaryKey)]) {
        primaryKey = [[cls alloc] primaryKey];
    }
    return primaryKey;
}

+ (NSString *)typeWithIvarName: (NSString *)ivarName class:(Class)cls
{
    NSDictionary *nameTypeDic = [self classIvarNameTypeDic:cls];
    return [nameTypeDic objectForKey:ivarName];
}

/**
 排序后的类名对应的成员变量数组, 用于和表格字段进行验证是否需要更新
 
 @param cls 类名
 @return 成员变量数组,
 */
+ (NSArray *)allTableSortedIvarNames:(Class)cls
{
    
    NSDictionary *dic = [self classIvarNameTypeDic:cls];
    NSArray *keys = dic.allKeys;
    keys = [keys sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        return [obj1 compare:obj2];
    }];
    return keys;
}


/**
 字典转对象

 @param data 字典数据
 
 @return 对象模型
// */
+ (id)assginDicToModelClass:(Class)cls withDictionary:(NSDictionary *)propertyKeyValue
{
    if (propertyKeyValue == nil) {
        return nil;
    }
    
    ///1.获取字典的key
    NSArray *propertyKeys = [propertyKeyValue allKeys];
    
    id model = [[cls alloc] init];
    ///2.循环遍历字典key, 并且动态生成实体类的setter方法，把字典的Value通过setter方法
    ///赋值给实体类的属性
    for (int i = 0; i < propertyKeys.count; i ++) {
        
        ///2.1 通过getSetterSelWithAttibuteName 方法来获取实体类的set方法
        NSString *propertyName = propertyKeys[i];
        SEL setSel = [self creatSetterWithPropertyName:propertyName];
        if ([model respondsToSelector:setSel]) {
            ///2.2 获取字典中key对应的value
            NSMutableDictionary *nameTypeDic = [self classIvarNameTypeDic:cls];
            NSString *nameType = [nameTypeDic objectForKey:propertyName];
            
            NSString *value = [NSString stringWithFormat:@"%@",propertyKeyValue[propertyName]];
            id modelValue;
            if ([@"d" isEqualToString:nameType]) {
                modelValue = [NSNumber numberWithDouble:[value doubleValue]];
            }else if([@"f" isEqualToString:nameType]) {
                modelValue = [NSNumber numberWithFloat:[value floatValue]];
            }else if([@"i" isEqualToString:nameType]) {
                modelValue = [NSNumber numberWithInteger:[value integerValue]];
            }else if([@"q" isEqualToString:nameType] || [@"Q" isEqualToString:nameType]) {
                modelValue = [NSNumber numberWithLong:[value longLongValue]];
            }else if([@"B" isEqualToString:nameType]) {
                modelValue = [NSNumber numberWithBool:[value boolValue]];
            }else if([@"NSData" isEqualToString:nameType]) {
                modelValue = [value dataUsingEncoding:NSUTF8StringEncoding];
            }else if([@"NSDictionary" isEqualToString:nameType] || [@"NSMutableDictionary" isEqualToString:nameType]) {
                NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
                NSError *err;
                modelValue = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers
                                                                      error:&err];
            }else if([@"NSArray" isEqualToString:nameType] || [@"NSMutableArray" isEqualToString:nameType]) {
                modelValue = [value componentsSeparatedByString:@","];
            }else{
                modelValue = value;
            }
            
            ///2.3 把值通过setter方法赋值给实体类的属性
            [model performSelectorOnMainThread:setSel
                                   withObject:modelValue
                                waitUntilDone:[NSThread isMainThread]];
        }
        
    }
    return model;
}

#pragma mark - 私有的方法
+ (NSDictionary *)ocTypeToSqliteTypeDic {
    return @{
             @"d": @"REAL", // double
             @"f": @"REAL", // float
             
             @"i": @"INTEGER",  // int
             @"q": @"INTEGER", // long
             @"Q": @"INTEGER", // long long
             @"B": @"INTEGER", // bool
             
             @"NSData": @"BLOB",
             @"NSDictionary": @"TEXT",
             @"NSMutableDictionary": @"TEXT",
             @"NSArray": @"TEXT",
             @"NSMutableArray": @"TEXT",
             
             @"NSString": @"TEXT"
             };
    
}

//是否有过滤主键字段
+ (BOOL)isIgnorePrimaryKey:(Class)cls
{
    //主键
    NSString *primaryKey = [XWXSqliteModelTool primaryKeyWithCls:cls];
    //过滤的字段
    NSArray *ignoreNames = nil;
    if ([cls conformsToProtocol:@protocol(XWXSqliteModelDelegate)] && [[cls new] respondsToSelector:@selector(ignoreIvarNames)]) {
        ignoreNames = [[cls alloc] ignoreIvarNames];
        if ([ignoreNames containsObject:primaryKey]) {
            NSLog(@"-------------->主键字段不能被过滤<--------------");
            return YES;
        }
    }
    return NO;
}

//是否有修改主键字段
+ (BOOL)isUpdatePrimaryKey:(Class)cls
{
    //主键
    NSString *primaryKey = [XWXSqliteModelTool primaryKeyWithCls:cls];
    NSArray *updateNames;
    if ([cls conformsToProtocol:@protocol(XWXSqliteModelDelegate)] && [[cls new] respondsToSelector:@selector(updateIvarNames)]) {
        updateNames = [[cls alloc] updateIvarNames];
        if ([updateNames containsObject:primaryKey]) {
            NSLog(@"-------------->主键字段不能被修改<--------------");
            return YES;
        }
    }
    return NO;
}

/**
 类成员变量名称
 @param cls 类
 @return 成员变量名称s
 */
+ (NSArray *)classIvarNames:(Class)cls
{
    //1.获取类的所有成员变量
    unsigned int outCount = 0;
    Ivar *ivarList = class_copyIvarList(cls, &outCount);
    
    if ([self isIgnorePrimaryKey:cls]) {
        return nil;
    }
    
    //过滤字段
    NSArray *ignoreNames = nil;
    if ([cls conformsToProtocol:@protocol(XWXSqliteModelDelegate)] && [[cls new] respondsToSelector:@selector(ignoreIvarNames)]) {
        ignoreNames = [[cls alloc] ignoreIvarNames];
    }
    
    NSMutableArray *ivarNames = [[NSMutableArray alloc] init];
    for (int i = 0; i < outCount; i++) {
        Ivar ivar = ivarList[i];
        //获取成员变量名称
        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        //过滤字段
        if ([ignoreNames containsObject:ivarName]) {
            continue;
        }
        
        if ([ivarName hasPrefix:@"_"] && ivarName.length > 1) {
            ivarName = [ivarName substringFromIndex:1];
        }

        [ivarNames addObject:ivarName];
    }
    
    return ivarNames;
}

//创建Set方法
+ (SEL) creatSetterWithPropertyName: (NSString *) propertyName
{
    if (propertyName.length == 0) {
        return nil;
    }
    
    //1.首字母大写
    NSString *firstLetter = [[propertyName substringToIndex:1] uppercaseString];
    NSString *subpropertyName = [propertyName substringFromIndex:1];
    
    //2.拼接上set关键字
    propertyName = [NSString stringWithFormat:@"set%@%@:", firstLetter,subpropertyName];
    
    //3.返回set方法
    return NSSelectorFromString(propertyName);
}

@end
